package com.barry.generator

import java.io.{File, FileOutputStream}
import java.util
import java.util.concurrent.CountDownLatch
import java.util.concurrent.locks.{Lock, ReentrantLock}
import java.util.{List => JavaList}

import com.alibaba.excel.write.metadata.WriteSheet
import com.alibaba.excel.{EasyExcelFactory, ExcelWriter}
import com.barry.bean.OperationData
import com.barry.dto.{ReportConfig, ReportOutput}
import com.barry.utils.{FileUtil, ZipUtil}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

/**
  * @ClassName ExcelReportAction
  * @Description Excel报表生成父类
  * @Author wangxuexing
  * @Date 2020/2/21 15:35
  * @Version 1.0
  */
abstract class ExcelReportAction[T] extends ReportAction{
  val lock = new ReentrantLock
  /**
    * 一个excel文件限制
    */
  private val ONE_EXCEL_LIMIT = 300000
  /**
    * 单页大小
    */
  private val ONE_PAGE_SIZE = 25000

  /**
    * 生成报表
    * @param reportId
    * @param config
    * @throws
    * @return
    */
  @throws[Exception]
  def generateReport(reportId: Long, config: ReportConfig): ReportOutput = {
    val allCount = allRecordsCount
    var targetFile:File = null;
    var excelCount = 1
    if (allCount > ONE_EXCEL_LIMIT) {
      if (allCount % ONE_EXCEL_LIMIT == 0) {
        excelCount = allCount / ONE_EXCEL_LIMIT
      } else {
        excelCount = allCount / ONE_EXCEL_LIMIT + 1
        targetFile = File.createTempFile(downloadFileName, ".zip")
      }
    } else {
      targetFile = File.createTempFile(downloadFileName, ".xlsx")
    }

    if (excelCount == 1){
      genReport(targetFile, config, allCount, 0, true, getPageNum(allCount))
    } else {
      val fileList = new util.ArrayList[File]()
      for(curExcel <- 0 until excelCount){
        var isLastExcel = false
        val pageNum = curExcel match {
          case a if((curExcel+1) == excelCount) => {
            isLastExcel = true
            val lastExcelCount = allCount - (ONE_EXCEL_LIMIT * (excelCount - 1))
            getPageNum(lastExcelCount)
          }
          case _ => getPageNum(ONE_EXCEL_LIMIT)
        }
        //TODO 定义生成Excel名称
        val excelFile = File.createTempFile(s"${downloadFileName}_${curExcel+1}_", ".xlsx")
        genReport(excelFile, config, allCount, curExcel, isLastExcel, pageNum)
        fileList.add(excelFile)
      }
      ZipUtil.zipFiles(fileList, targetFile)
    }

    //上传云存储
    val targetUrl = saveReport(targetFile)
    //返回云存储路径
    ReportOutput(targetUrl)
  }

  /**
    * 获取所有查询记录条数
    *
    * @return
    */
  def allRecordsCount: Int

  /**
    * 生成报表
    *
    * @param targetFile
    * @param config
    * @param allCount
    * @param currentExcel
    * @param isLastExcel
    * @param pageNum
    * @throws Exception
    */
  @throws[Exception]
  private def genReport(targetFile: File,
                        config: ReportConfig,
                        allCount: Int,
                        currentExcel: Int,
                        isLastExcel: Boolean,
                        pageNum: Int): Unit = {
    val outputStream = new FileOutputStream(targetFile)
    val templateInputStream = FileUtil.getInputStream(config.templateName)
    val excelWriter = EasyExcelFactory.write(outputStream, classOf[OperationData])
      .withTemplate(templateInputStream)
      .needHead(false)
      .build
    //这里注意 如果同一个sheet只要创建一次
    val writeSheet = EasyExcelFactory.writerSheet(config.sheetName).build
    val latch = new CountDownLatch(pageNum)
    //多线程分页查询数据
    for(curPage <- 0 until pageNum){
      Future {
        val count = getCurrentPageCount(allCount, currentExcel, isLastExcel, pageNum, curPage)
        write(lock, getReportDataByPage(currentExcel+curPage, ONE_PAGE_SIZE, count), excelWriter, writeSheet)
        latch.countDown()
      }
    }
    latch.await()
    //千万别忘记finish 会帮忙关闭流
    excelWriter.finish()
  }

  /**
    * 分页获取数据
    *
    * @param currentPage
    * @param pageSize
    * @return
    */
  def getReportDataByPage(currentPage: Int, pageSize: Int, count: Int): JavaList[T]

  /**
    * 获取页数
    *
    * @param count
    * @return
    */
  private def getPageNum(count: Int) = {
    count % ONE_PAGE_SIZE match {
      case 0 => count / ONE_PAGE_SIZE
      case _ => count / ONE_PAGE_SIZE + 1
    }
  }

  /**
    * 获取当前页记录数量
    * @param allCount
    * @param currentExcel
    * @param isLastExcel
    * @param pageNum
    * @param curPage
    * @return
    */
  private def getCurrentPageCount(allCount: Int,
                                  currentExcel: Int,
                                  isLastExcel: Boolean,
                                  pageNum: Int,
                                  curPage: Int): Int ={
    isLastExcel match {
      case true => {
        curPage match {
          case a if(curPage+1 == pageNum) => {
            val currExcelCount = allCount-ONE_EXCEL_LIMIT*currentExcel
            currExcelCount-ONE_PAGE_SIZE*curPage
          }
          case _ => ONE_PAGE_SIZE
        }
      }
      case false => {
        curPage match {
          case a if(curPage+1 == pageNum) => ONE_EXCEL_LIMIT-ONE_PAGE_SIZE*curPage
          case _ => ONE_PAGE_SIZE
        }
      }
    }
  }

  /**
    * 线程安全写入数据
    *
    * @param lock Lock
    * @param excelWriter
    * @param data
    * @param writeSheet
    */
  private def write[T](lock: Lock, data: JavaList[T], excelWriter: ExcelWriter, writeSheet: WriteSheet): Unit = {
    lock.lock()
    try {
      excelWriter.write(data, writeSheet)
    } finally {
      lock.unlock()
    }
  }

  /**
    * 上传云存储
    * @param file
    * @return
    */
  def saveReport(file: File):String ={
    //TODO
    //file.deleteOnExit()
    ""
  }

}
